﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Data;
using System.Data.Common;
using System.Globalization;
using ResourceBlender.DAL;
using ResourceBlender.Express.Converters;
using ResourceBlender.Core;

namespace ResourceBlender.Express {
    /// <summary>
    /// A window which shows all identical translations for a resource
    /// </summary>
    public partial class WinDuplicateDetails : Window {
        private ObservableSortedDictionary<string, CultureInfo> languages;
        private string language, // the language of the translation for which to show dupes
                       translation; // the duplicate translation
        private DataTemplate ascSorted,
                             descSorted;

        /// <summary>
        /// Initializes a new <see cref="WinDuplicateDetails"/>.
        /// </summary>
        /// <param name="languages">The languages extra languages for which to show duplicates.</param>
        /// <param name="resourceLanguage">The language for which to fnd duplicates.</param>
        /// <param name="resourceTranslation">The resource translation.</param>
        public WinDuplicateDetails(ObservableSortedDictionary<string, CultureInfo> languages, string resourceLanguage, string resourceTranslation) {
            InitializeComponent();
            ascSorted = (DataTemplate)FindResource("HeaderTemplateAsc");
            descSorted = (DataTemplate)FindResource("HeaderTemplateDesc");
            this.languages = languages;
            this.language = resourceLanguage;
            this.translation = resourceTranslation;
            StringBuilder sqlBuffer = new StringBuilder();
            GridViewColumn col;
            foreach(KeyValuePair<string, CultureInfo> language in languages) {
                col = new GridViewColumn();
                col.Header = language.Value.EnglishName;
                col.DisplayMemberBinding = new Binding(language.Key);
                col.Width = 120;
                grdResources.Columns.Add(col);
            }
            FindDuplicates();
        }

        /// <summary>
        /// Shows all languages in which a duplicate resource is available.
        /// </summary>
        private void FindDuplicates() {
            BackgroundWorker worker = new BackgroundWorker();
            worker.DoWork += (s, dwe) => {
                dwe.Result = ResourceBlenderDAL.GetDuplicateResources(languages.Keys.ToArray(), language, translation);
            };
            worker.RunWorkerCompleted += (s, rwe) => {
                SetStatus(null, false);
                if(rwe.Error != null) {
                    DialogBox.ShowAlert(this, "Could not connect to database - " + rwe.Error.Message, "Database Error");
                } else {
                    lstGrid.DataContext = rwe.Result;
                }
                CheckDifferences(rwe.Result as DataTable);
            };
            SetStatus("Loading resources...", true);
            worker.RunWorkerAsync();
        }

        /// <summary>
        /// Checks each column for differences and shows a warning if resources are not identical.
        /// </summary>
        /// <param name="resourceTable">The resource table.</param>
        private void CheckDifferences(DataTable resourceTable) {
            IEnumerable<string> conflictingLanguages = ResourceBlenderBLL.DoResourcesConflict(resourceTable, 3);
            if(conflictingLanguages.Any()) {
                warningPanel.Visibility = Visibility.Visible;
                identicalPanel.Visibility = Visibility.Collapsed;
                lblConflictingLanguages.Text = new LanguageCodeListStringifier().Convert(conflictingLanguages, typeof(string), null, CultureInfo.InvariantCulture).ToString();
            } else {
                warningPanel.Visibility = Visibility.Collapsed;
                identicalPanel.Visibility = Visibility.Visible;
            }

        }

        /// <summary>
        /// Determines whether the 'MergeDuplicates' command can be executed.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.CanExecuteRoutedEventArgs"/> instance containing the event data.</param>
        private void CanExecuteMergeDuplicates(object sender, CanExecuteRoutedEventArgs e) {
            e.CanExecute = this.IsLoaded && lstGrid.SelectedItems.Count > 1;
        }

        /// <summary>
        /// Merges the selected resources.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void MergeDuplicates(object sender, ExecutedRoutedEventArgs e) {
            bool deleteUnused = false;
            if(e.Parameter != null) {
                bool.TryParse(e.Parameter.ToString(), out deleteUnused);
            }
            MergeDuplicates(deleteUnused);
        }

        /// <summary>
        /// Merges the selected resources.
        /// </summary>
        /// <param name="deleteUnusedResource"><c>true</c> if the unused resource remaining should be deleted.</param>
        private void MergeDuplicates(bool deleteUnusedResource) {
            DataTable resourceTable = lstGrid.DataContext as DataTable;
            if(resourceTable == null)
                return;
            List<int> selectedIds = new List<int>();
            DataTable selectedRows = resourceTable.Clone();
            foreach(DataRowView rowView in lstGrid.SelectedItems) {
                selectedIds.Add(int.Parse(rowView.Row.ItemArray[0].ToString()));
                selectedRows.ImportRow(rowView.Row);
            }

            int preferredResource = -1;
            if(ResourceBlenderBLL.DoResourcesConflict(selectedRows, 3).Any()) { // conflicts between selected rows
                selectedIds.Sort();
                WinListBoxSelection dlg = new WinListBoxSelection("Conflict Resolution", "Choose which resource to use if there are multiple translations in some languages", selectedIds.ConvertAll<string>(i => i.ToString()));
                dlg.Owner = this;
                if(dlg.ShowDialog() != true) {
                    return;
                }
                if(!int.TryParse(dlg.SelectedItem, out preferredResource)) {
                    DialogBox.ShowAlert(this, "Invalid ID", "Error");
                }
            } else {
                preferredResource = selectedIds[0];
            }
            BackgroundWorker worker = new BackgroundWorker();
            worker.DoWork += (s, dwe) => {
                ResourceBlenderDAL.MergeResources(selectedIds, preferredResource);
                if(deleteUnusedResource) {
                    ResourceBlenderDAL.DeleteResources(selectedIds.Except(new int[] { preferredResource }));
                }
            };
            worker.RunWorkerCompleted += (s, rwe) => {
                SetStatus(null, false);
                if(rwe.Result != null) {
                    DialogBox.ShowAlert(this, "Error: Could not connect to database - " + rwe.Result.ToString(), "Database Error");
                }
                FindDuplicates();
            };
            SetStatus("Loading...", true);
            worker.RunWorkerAsync();
        }

        /// <summary>
        /// Shows the elements used by a resource.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void ExpandDuplicateDetails(object sender, RoutedEventArgs e) {
            TreeViewItem treElementDetails = e.OriginalSource as TreeViewItem;
            TreeViewItem defaultNode = treElementDetails.Items[0] as TreeViewItem;
            if(defaultNode.Tag == null) {
                return;
            }
            int resourceId = -1;
            if(!int.TryParse(treElementDetails.Tag.ToString(), out resourceId)) {
                DialogBox.ShowAlert(this, "Invalid Resource ID", "Error");
                return;
            }
            BackgroundWorker worker = new BackgroundWorker();
            worker.DoWork += (s, dwe) => {
                Dictionary<string, List<string>> usage = ResourceBlenderDAL.GetResourceUsage(resourceId);
                dwe.Result = usage;
            };
            worker.RunWorkerCompleted += (s, rwe) => {
                SetStatus(null, false);
                if(rwe.Error != null) {
                    DialogBox.ShowAlert(this, rwe.Error.Message, "Error");
                } else {
                    Dictionary<string, List<string>> applications = rwe.Result as Dictionary<string, List<string>>;
                    if(applications != null && applications.Count > 0) {
                        treElementDetails.Items.Clear();
                        foreach(KeyValuePair<string, List<string>> app in applications) {
                            TreeViewItem itmApp = new TreeViewItem();
                            itmApp.Header = app.Key;
                            itmApp.FontWeight = FontWeights.Bold;
                            treElementDetails.Items.Add(itmApp);
                            foreach(string element in app.Value) {
                                TreeViewItem itmElement = new TreeViewItem();
                                itmElement.Header = string.Format(element);
                                itmApp.FontWeight = FontWeights.Normal;
                                itmApp.Items.Add(itmElement);
                            }
                        }
                    } else {
                        defaultNode.Tag = null;
                    }
                }
            };
            SetStatus("Loading...", true);
            worker.RunWorkerAsync();
        }

        /// <summary>
        /// Determines whether all resources can be selected.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.CanExecuteRoutedEventArgs"/> instance containing the event data.</param>
        private void CanExecuteSelectAll(object sender, CanExecuteRoutedEventArgs e) {
            e.CanExecute = this.IsLoaded && lstGrid.HasItems;
        }

        /// <summary>
        /// Selects all resources.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void SelectAllResources(object sender, ExecutedRoutedEventArgs e) {
            lstGrid.SelectAll();
        }

        /// <summary>
        /// Sorts the grid.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void OnColumnHeaderClick(object sender, RoutedEventArgs e) {
            if(e.OriginalSource is GridViewColumnHeader) { // sort the grid
                GridViewColumn column = ((GridViewColumnHeader)e.OriginalSource).Column;
                string sortPath = column.DisplayMemberBinding == null ? (column.CellTemplate.DataType ?? string.Empty).ToString() : (column.DisplayMemberBinding as Binding).Path.Path;
                if(sortPath.Length == 0) {
                    return; // no field specified
                }
                if(column.HeaderTemplate == ascSorted) {
                    column.HeaderTemplate = null;
                    (lstGrid.DataContext as DataTable).DefaultView.Sort = sortPath + " DESC"; ;
                } else if(column.HeaderTemplate == descSorted) {
                    column.HeaderTemplate = ascSorted;
                    (lstGrid.DataContext as DataTable).DefaultView.Sort = "";
                } else {
                    column.HeaderTemplate = ascSorted;
                    (lstGrid.DataContext as DataTable).DefaultView.Sort = sortPath + " ASC";
                }
            }
        }

        #region Editing and Deleting
        /// <summary>
        /// Determines whether the EditResource or DeleteResource commands can be executed.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.CanExecuteRoutedEventArgs"/> instance containing the event data.</param>
        private void CanEditOrDelete(object sender, CanExecuteRoutedEventArgs e) {
            e.CanExecute = this.IsLoaded && lstGrid.SelectedValue != null;
        }

        /// <summary>
        /// Fires the EditResource command when the grid is double clicked.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void FireEditResource(object sender, RoutedEventArgs e) {
            if(lstGrid.SelectedValue != null) {
                ResourceBlenderCommands.Edit.Execute(null, null);
            }
        }

        /// <summary>
        /// Opens a dialog to edits the selected resource (or first selected resource if multiple items are selected).
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void EditResource(object sender, ExecutedRoutedEventArgs e) {
            int resourceId = -1;
            if(!int.TryParse(lstGrid.SelectedValue.ToString(), out resourceId)) {
                DialogBox.ShowAlert(this, "Error #459: Invalid resource", "Error");
            } else {
                WinAddResource dlg = new WinAddResource(resourceId);
                dlg.Owner = this;
                dlg.ShowDialog();
                FindDuplicates();
            }
        }

        /// <summary>
        /// Shows a prompt to confirm deletion of a resource, then deletes it from the database.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void DeleteResource(object sender, ExecutedRoutedEventArgs e) {
            if(lstGrid.SelectedItem == null) {
                return;
            }

            // check which resources are selected and prompt.
            List<int> resourcesToDelete = new List<int>();
            foreach(DataRowView rowView in lstGrid.SelectedItems) {
                try {
                    int numElements = int.Parse(rowView.Row.ItemArray[2].ToString()),
                        resourceId = int.Parse(rowView.Row.ItemArray[0].ToString());
                    if(numElements > 0) {
                        DialogBox.ShowAlert(this, string.Format("Resource #{0} is currently in use and must be merged with another translation before deletion.", resourceId), "Error");
                    } else {
                        resourcesToDelete.Add(resourceId);
                    }
                } catch {
                    DialogBox.ShowAlert(this, "Error deleting resource", "Error");
                }
            }

            BackgroundWorker worker = new BackgroundWorker();
            worker.DoWork += (s, dwe) => {
                ResourceBlenderDAL.DeleteResources(resourcesToDelete);
            };
            worker.RunWorkerCompleted += (s, rwe) => {
                SetStatus(null, false);
                if(rwe.Error != null) {
                    DialogBox.ShowAlert(this, rwe.Error.Message, "Error");
                }
                FindDuplicates();
            };
            if(resourcesToDelete.Count > 0 && DialogBox.Show(this, string.Format("{0} {1} will be deleted.\n\nAre you sure?", resourcesToDelete.Count.ToString(), resourcesToDelete.Count == 1 ? "resource" : "resources"), "Confirm Delete", DialogBox.DialogBoxType.YesNo, DialogBox.DialogBoxIcon.Warning, DialogBox.DialogBoxButton.No) == DialogBox.DialogBoxButton.Yes) {
                worker.RunWorkerAsync();
                SetStatus("Deleting...", true);
            }
        }
        #endregion

        /// <summary>
        /// Sets the text on the status message.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="showProgress"><c>true</c> if the progress bar should be shown, otherwise <c>false</c>.</param>
        private void SetStatus(string message, bool showProgress) {
            prgStatus.Visibility = showProgress ? Visibility.Visible : Visibility.Hidden;
            if(message == null) {
                lblStatus.Visibility = Visibility.Hidden;
            } else {
                lblStatus.Visibility = Visibility.Visible;
                lblStatus.Content = message;
            }
        }
    }
}
